home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-13
/
mg2a_src.zip
/
LINE.C
< prev
next >
Wrap
C/C++ Source or Header
|
1988-08-23
|
17KB
|
610 lines
/*
* Text line handling.
* The functions in this file
* are a general set of line management
* utilities. They are the only routines that
* touch the text. They also touch the buffer
* and window structures, to make sure that the
* necessary updating gets done. There are routines
* in this file that handle the kill buffer too.
* It isn't here for any good reason.
*
* Note that this code only updates the dot and
* mark values in the window list. Since all the code
* acts on the current window, the buffer that we
* are editing must be being displayed, which means
* that "b_nwnd" is non zero, which means that the
* dot and mark values in the buffer headers are
* nonsense.
*/
#include "def.h"
/* number of bytes member is from start of structure type */
/* should be computed at compile time */
#ifndef OFFSET
#define OFFSET(type,member) ((char *)&(((type *)0)->member)-(char *)((type *)0))
#endif
#ifndef NBLOCK
#define NBLOCK 16 /* Line block chunk size */
#endif
#ifndef KBLOCK
#define KBLOCK 256 /* Kill buffer block size. */
#endif
static char *kbufp = NULL; /* Kill buffer data. */
static RSIZE kused = 0; /* # of bytes used in KB. */
static RSIZE ksize = 0; /* # of bytes allocated in KB. */
static RSIZE kstart = 0; /* # of first used byte in KB. */
/*
* This routine allocates a block of memory large enough to hold a LINE
* containing "used" characters. The block is rounded up to whatever
* needs to be allocated. (use lallocx for lines likely to grow.)
* Return a pointer to the new block, or NULL if there isn't
* any memory left. Print a message in the message line if no space.
*/
LINE *
lalloc(used) register int used; {
register LINE *lp;
register int size;
/* any padding at the end of the structure is used */
if((size = used + OFFSET(LINE, l_text[0])) < sizeof(LINE))
size = sizeof(LINE);
#ifdef MALLOCROUND
MALLOCROUND(size); /* round up to a size optimal to malloc */
#endif
if((lp = (LINE *)malloc((unsigned)size)) == NULL) {
ewprintf("Can't get %d bytes", size);
return (LINE *)NULL;
}
lp->l_size = size - OFFSET(LINE, l_text[0]);
lp->l_used = used;
return lp;
}
/*
* Like lalloc, only round amount desired up because this line will
* probably grow. We always make room for at least one more char.
* (thus making 0 not a special case anymore.)
*/
LINE *
lallocx(used)
int used;
{
register int size;
register LINE *lp;
size = (NBLOCK+used) & ~(NBLOCK-1);
if((lp = lalloc(size)) != NULL) lp->l_used = used;
return lp;
}
/*
* Delete line "lp". Fix all of the
* links that might point at it (they are
* moved to offset 0 of the next line.
* Unlink the line from whatever buffer it
* might be in. Release the memory. The
* buffers are updated too; the magic conditions
* described in the above comments don't hold
* here.
*/
VOID
lfree(lp) register LINE *lp; {
register BUFFER *bp;
register WINDOW *wp;
for(wp = wheadp; wp != NULL; wp = wp->w_wndp) {
if (wp->w_linep == lp)
wp->w_linep = lp->l_fp;
if (wp->w_dotp == lp) {
wp->w_dotp = lp->l_fp;
wp->w_doto = 0;
}
if (wp->w_markp == lp) {
wp->w_markp = lp->l_fp;
wp->w_marko = 0;
}
}
for(bp = bheadp; bp != NULL; bp = bp->b_bufp) {
if (bp->b_nwnd == 0) {
if (bp->b_dotp == lp) {
bp->b_dotp = lp->l_fp;
bp->b_doto = 0;
}
if (bp->b_markp == lp) {
bp->b_markp = lp->l_fp;
bp->b_marko = 0;
}
}
}
lp->l_bp->l_fp = lp->l_fp;
lp->l_fp->l_bp = lp->l_bp;
free((char *) lp);
}
/*
* This routine gets called when
* a character is changed in place in the
* current buffer. It updates all of the required
* flags in the buffer and window system. The flag
* used is passed as an argument; if the buffer is being
* displayed in more than 1 window we change EDIT to
* HARD. Set MODE if the mode line needs to be
* updated (the "*" has to be set).
*/
VOID
lchange(flag) register int flag; {
register WINDOW *wp;
if ((curbp->b_flag&BFCHG) == 0) { /* First change, so */
flag |= WFMODE; /* update mode lines. */
curbp->b_flag |= BFCHG;
}
for(wp = wheadp; wp != NULL; wp = wp->w_wndp) {
if (wp->w_bufp == curbp) {
wp->w_flag |= flag;
if(wp != curwp) wp->w_flag |= WFHARD;
}
}
}
/*
* Insert "n" copies of the character "c"
* at the current location of dot. In the easy case
* all that happens is the text is stored in the line.
* In the hard case, the line has to be reallocated.
* When the window list is updated, take special
* care; I screwed it up once. You always update dot
* in the current window. You update mark, and a
* dot in another window, if it is greater than
* the place where you did the insert. Return TRUE
* if all is well, and FALSE on errors.
*/
linsert(n, c)
int n;
{
register char *cp1;
register char *cp2;
register LINE *lp1;
LINE *lp2;
LINE *lp3;
register int doto;
register RSIZE i;
WINDOW *wp;
lchange(WFEDIT);
lp1 = curwp->w_dotp; /* Current line */
if (lp1 == curbp->b_linep) { /* At the end: special */
/* (now should only happen in empty buffer */
if (curwp->w_doto != 0) {
ewprintf("bug: linsert");
return FALSE;
}
if ((lp2=lallocx(n)) == NULL) /* Allocate new line */
return FALSE;
lp3 = lp1->l_bp; /* Previous line */
lp3->l_fp = lp2; /* Link in */
lp2->l_fp = lp1;
lp1->l_bp = lp2;
lp2->l_bp = lp3;
for (i=0; i<n; ++i)
lp2->l_text[i] = c;
for(wp = wheadp; wp != NULL; wp = wp->w_wndp) {
if (wp->w_linep == lp1)
wp->w_linep = lp2;
if (wp->w_dotp == lp1)
wp->w_dotp = lp2;
if (wp->w_markp == lp1)
wp->w_markp = lp2;
}
/*NOSTRICT*/
curwp->w_doto = n;
return TRUE;
}
doto = curwp->w_doto; /* Save for later. */
/*NOSTRICT (2) */
if (lp1->l_used+n > lp1->l_size) { /* Hard: reallocate */
if ((lp2=lallocx(lp1->l_used+n)) == NULL)
return FALSE;
cp1 = &lp1->l_text[0];
cp2 = &lp2->l_text[0];
while (cp1 != &lp1->l_text[doto])
*cp2++ = *cp1++;
/*NOSTRICT*/
cp2 += n;
while (cp1 != &lp1->l_text[lp1->l_used])
*cp2++ = *cp1++;
lp1->l_bp->l_fp = lp2;
lp2->l_fp = lp1->l_fp;
lp1->l_fp->l_bp = lp2;
lp2->l_bp = lp1->l_bp;
free((char *) lp1);
} else { /* Easy: in place */
lp2 = lp1; /* Pretend new line */
/*NOSTRICT*/
lp2->l_used += n;
cp2 = &lp1->l_text[lp1->l_used];
cp1 = cp2-n;
while (cp1 != &lp1->l_text[doto])
*--cp2 = *--cp1;
}
for (i=0; i<n; ++i) /* Add the characters */
lp2->l_text[doto+i] = c;
for(wp = wheadp; wp != NULL; wp = wp->w_wndp) {
if (wp->w_linep == lp1)
wp->w_linep = lp2;
if (wp->w_dotp == lp1) {
wp->w_dotp = lp2;
if (wp==curwp || wp->w_doto>doto)
/*NOSTRICT*/
wp->w_doto += n;
}
if (wp->w_markp == lp1) {
wp->w_markp = lp2;
if (wp->w_marko > doto)
/*NOSTRICT*/
wp->w_marko += n;
}
}
return TRUE;
}
/*
* Insert a newline into the buffer
* at the current location of dot in the current
* window. The funny ass-backwards way is no longer used.
*/
lnewline()
{
register LINE *lp1;
register LINE *lp2;
register int doto;
register int nlen;
WINDOW *wp;
lchange(WFHARD);
lp1 = curwp->w_dotp; /* Get the address and */
doto = curwp->w_doto; /* offset of "." */
if(doto == 0) { /* avoid unnessisary copying */
if((lp2 = lallocx(0)) == NULL) /* new first part */
return FALSE;
lp2->l_bp = lp1->l_bp;
lp1->l_bp->l_fp = lp2;
lp2->l_fp = lp1;
lp1->l_bp = lp2;
for(wp = wheadp; wp!=NULL; wp = wp->w_wndp)
if(wp->w_linep == lp1) wp->w_linep = lp2;
return TRUE;
}
nlen = llength(lp1) - doto; /* length of new part */
if((lp2=lallocx(nlen)) == NULL) /* New second half line */
return FALSE;
if(nlen!=0) bcopy(&lp1->l_text[doto], &lp2->l_text[0], nlen);
lp1->l_used = doto;
lp2->l_bp = lp1;
lp2->l_fp = lp1->l_fp;
lp1->l_fp = lp2;
lp2->l_fp->l_bp = lp2;
for(wp = wheadp; wp != NULL; wp = wp->w_wndp) { /* Windows */
if (wp->w_dotp == lp1 && wp->w_doto >= doto) {
wp->w_dotp = lp2;
wp->w_doto -= doto;
}
if (wp->w_markp == lp1 && wp->w_marko >= doto) {
wp->w_markp = lp2;
wp->w_marko -= doto;
}
}
return TRUE;
}
/*
* This function deletes "n" bytes,
* starting at dot. It understands how do deal
* with end of lines, etc. It returns TRUE if all
* of the characters were deleted, and FALSE if
* they were not (because dot ran into the end of
* the buffer. The "kflag" indicates either no insertion,
* or direction of insertion into the kill buffer.
*/
ldelete(n, kflag) RSIZE n; {
register char *cp1;
register char *cp2;
register LINE *dotp;
register int doto;
register RSIZE chunk;
WINDOW *wp;
/*
* HACK - doesn't matter, and fixes back-over-nl bug for empty
* kill buffers.
*/
if (kused == kstart) kflag = KFORW;
while (n != 0) {
dotp = curwp->w_dotp;
doto = curwp->w_doto;
if (dotp == curbp->b_linep) /* Hit end of buffer. */
return FALSE;
chunk = dotp->l_used-doto; /* Size of chunk. */
if (chunk > n)
chunk = n;
if (chunk == 0) { /* End of line, merge. */
if(dotp == lback(curbp->b_linep))
return FALSE; /* End of buffer. */
lchange(WFHARD);
if (ldelnewline() == FALSE
|| (kflag!=KNONE && kinsert('\n', kflag)==FALSE))
return FALSE;
--n;
continue;
}
lchange(WFEDIT);
cp1 = &dotp->l_text[doto]; /* Scrunch text. */
cp2 = cp1 + chunk;
if (kflag == KFORW) {
while (ksize - kused < chunk)
if (kgrow(FALSE) == FALSE) return FALSE;
bcopy(cp1, &(kbufp[kused]), (int) chunk);
kused += chunk;
} else if (kflag == KBACK) {
while (kstart < chunk)
if (kgrow(TRUE) == FALSE) return FALSE;
bcopy(cp1, &(kbufp[kstart-chunk]), (int) chunk);
kstart -= chunk;
} else if (kflag != KNONE) panic("broken ldelete call");
while (cp2 != &dotp->l_text[dotp->l_used])
*cp1++ = *cp2++;
dotp->l_used -= (int) chunk;
for(wp = wheadp; wp != NULL; wp = wp->w_wndp ) {
if (wp->w_dotp==dotp && wp->w_doto>=doto) {
/*NOSTRICT*/
wp->w_doto -= chunk;
if (wp->w_doto < doto)
wp->w_doto = doto;
}
if (wp->w_markp==dotp && wp->w_marko>=doto) {
/*NOSTRICT*/
wp->w_marko -= chunk;
if (wp->w_marko < doto)
wp->w_marko = doto;
}
}
n -= chunk;
}
return TRUE;
}
/*
* Delete a newline. Join the current line
* with the next line. If the next line is the magic
* header line always return TRUE; merging the last line
* with the header line can be thought of as always being a
* successful operation, even if nothing is done, and this makes
* the kill buffer work "right". Easy cases can be done by
* shuffling data around. Hard cases require that lines be moved
* about in memory. Return FALSE on error and TRUE if all
* looks ok.
*/
ldelnewline() {
register LINE *lp1;
register LINE *lp2;
register WINDOW *wp;
LINE *lp3;
lp1 = curwp->w_dotp;
lp2 = lp1->l_fp;
if (lp2 == curbp->b_linep) /* At the buffer end. */
return TRUE;
if (lp2->l_used <= lp1->l_size - lp1->l_used) {
bcopy(&lp2->l_text[0], &lp1->l_text[lp1->l_used], lp2->l_used);
for(wp = wheadp; wp != NULL; wp = wp->w_wndp) {
if (wp->w_linep == lp2)
wp->w_linep = lp1;
if (wp->w_dotp == lp2) {
wp->w_dotp = lp1;
wp->w_doto += lp1->l_used;
}
if (wp->w_markp == lp2) {
wp->w_markp = lp1;
wp->w_marko += lp1->l_used;
}
}
lp1->l_used += lp2->l_used;
lp1->l_fp = lp2->l_fp;
lp2->l_fp->l_bp = lp1;
free((char *) lp2);
return TRUE;
}
if ((lp3=lalloc(lp1->l_used + lp2->l_used)) == NULL)
return FALSE;
bcopy(&lp1->l_text[0], &lp3->l_text[0], lp1->l_used);
bcopy(&lp2->l_text[0], &lp3->l_text[lp1->l_used], lp2->l_used);
lp1->l_bp->l_fp = lp3;
lp3->l_fp = lp2->l_fp;
lp2->l_fp->l_bp = lp3;
lp3->l_bp = lp1->l_bp;
for(wp = wheadp; wp != NULL; wp = wp->w_wndp) {
if (wp->w_linep==lp1 || wp->w_linep==lp2)
wp->w_linep = lp3;
if (wp->w_dotp == lp1)
wp->w_dotp = lp3;
else if (wp->w_dotp == lp2) {
wp->w_dotp = lp3;
wp->w_doto += lp1->l_used;
}
if (wp->w_markp == lp1)
wp->w_markp = lp3;
else if (wp->w_markp == lp2) {
wp->w_markp = lp3;
wp->w_marko += lp1->l_used;
}
}
free((char *) lp1);
free((char *) lp2);
return TRUE;
}
/*
* Replace plen characters before dot with argument string.
* Control-J characters in st are interpreted as newlines.
* There is a casehack disable flag (normally it likes to match
* case of replacement to what was there).
*/
lreplace(plen, st, f)
register RSIZE plen; /* length to remove */
char *st; /* replacement string */
int f; /* case hack disable */
{
register RSIZE rlen; /* replacement length */
register int rtype; /* capitalization */
register int c; /* used for random characters */
register int doto; /* offset into line */
/*
* Find the capitalization of the word that was found.
* f says use exact case of replacement string (same thing that
* happens with lowercase found), so bypass check.
*/
/*NOSTRICT*/
(VOID) backchar(FFARG | FFRAND, (int) plen);
rtype = _L;
c = lgetc(curwp->w_dotp, curwp->w_doto);
if (ISUPPER(c)!=FALSE && f==FALSE) {
rtype = _U|_L;
if (curwp->w_doto+1 < llength(curwp->w_dotp)) {
c = lgetc(curwp->w_dotp, curwp->w_doto+1);
if (ISUPPER(c) != FALSE) {
rtype = _U;
}
}
}
/*
* make the string lengths match (either pad the line
* so that it will fit, or scrunch out the excess).
* be careful with dot's offset.
*/
rlen = strlen(st);
doto = curwp->w_doto;
if (plen > rlen)
(VOID) ldelete((RSIZE) (plen-rlen), KNONE);
else if (plen < rlen) {
if (linsert((int)(rlen-plen), ' ') == FALSE)
return FALSE;
}
curwp->w_doto = doto;
/*
* do the replacement: If was capital, then place first
* char as if upper, and subsequent chars as if lower.
* If inserting upper, check replacement for case.
*/
while ((c = CHARMASK(*st++)) != '\0') {
if ((rtype&_U)!=0 && ISLOWER(c)!=0)
c = TOUPPER(c);
if (rtype == (_U|_L))
rtype = _L;
if (c == CCHR('J')) {
if (curwp->w_doto == llength(curwp->w_dotp))
(VOID) forwchar(FFRAND, 1);
else {
if (ldelete((RSIZE) 1, KNONE) != FALSE)
(VOID) lnewline();
}
} else if (curwp->w_dotp == curbp->b_linep) {
(VOID) linsert(1, c);
} else if (curwp->w_doto == llength(curwp->w_dotp)) {
if (ldelete((RSIZE) 1, KNONE) != FALSE)
(VOID) linsert(1, c);
} else
lputc(curwp->w_dotp, curwp->w_doto++, c);
}
lchange(WFHARD);
return (TRUE);
}
/*
* Delete all of the text
* saved in the kill buffer. Called by commands
* when a new kill context is being created. The kill
* buffer array is released, just in case the buffer has
* grown to immense size. No errors.
*/
VOID
kdelete() {
if (kbufp != NULL) {
free((char *) kbufp);
kbufp = NULL;
kstart = kused = ksize = 0;
}
}
/*
* Insert a character to the kill buffer,
* enlarging the buffer if there isn't any room. Always
* grow the buffer in chunks, on the assumption that if you
* put something in the kill buffer you are going to put
* more stuff there too later. Return TRUE if all is
* well, and FALSE on errors. Print a message on
* errors. Dir says whether to put it at back or front.
*/
kinsert(c, dir) {
if (kused == ksize && dir == KFORW && kgrow(FALSE) == FALSE)
return FALSE;
if (kstart == 0 && dir == KBACK && kgrow(TRUE) == FALSE)
return FALSE;
if (dir == KFORW) kbufp[kused++] = c;
else if (dir == KBACK) kbufp[--kstart] = c;
else panic("broken kinsert call"); /* Oh shit! */
return (TRUE);
}
/*
* kgrow - just get more kill buffer for the callee. back is true if
* we are trying to get space at the beginning of the kill buffer.
*/
kgrow(back) {
register int nstart;
register char *nbufp;
if ((unsigned)(ksize+KBLOCK) <= (unsigned)ksize) {
/* probably 16 bit unsigned */
ewprintf("Kill buffer size at maximum");
return FALSE;
}
if ((nbufp=malloc((unsigned)(ksize+KBLOCK))) == NULL) {
ewprintf("Can't get %ld bytes", (long)(ksize+KBLOCK));
return FALSE;
}
nstart = (back == TRUE) ? (kstart + KBLOCK) : (KBLOCK / 4) ;
bcopy(&(kbufp[kstart]), &(nbufp[nstart]), (int) (kused-kstart));
if (kbufp != NULL)
free((char *) kbufp);
kbufp = nbufp;
ksize += KBLOCK;
kused = kused - kstart + nstart;
kstart = nstart;
return TRUE;
}
/*
* This function gets characters from
* the kill buffer. If the character index "n" is
* off the end, it returns "-1". This lets the caller
* just scan along until it gets a "-1" back.
*/
kremove(n) {
if (n < 0 || n + kstart >= kused)
return -1;
return CHARMASK(kbufp[n + kstart]);
}